﻿(function($) {
	var isDown = false;
	function drag(e) {
		var opts = $.data(e.data.target, "draggable").options;
		var dragData = e.data;
		var left = dragData.startLeft + e.pageX - dragData.startX;
		var top = dragData.startTop + e.pageY - dragData.startY;
		if (opts.deltaX != null && opts.deltaX != undefined) {
			left = e.pageX + opts.deltaX;
		}
		if (opts.deltaY != null && opts.deltaY != undefined) {
			top = e.pageY + opts.deltaY;
		}
		if (e.data.parent != document.body) {
			left += $(e.data.parent).scrollLeft();
			top += $(e.data.parent).scrollTop();
		}
		if (opts.axis == "h") {
			dragData.left = left;
		} else {
			if (opts.axis == "v") {
				dragData.top = top;
			} else {
				dragData.left = left;
				dragData.top = top;
			}
		}
	}
	;
	function applyDrag(e) {
		var opts = $.data(e.data.target, "draggable").options;
		var proxy = $.data(e.data.target, "draggable").proxy;
		if (proxy) {
			proxy.css("cursor", opts.cursor);
		} else {
			proxy = $(e.data.target);
			$.data(e.data.target, "draggable").handle
					.css("cursor", opts.cursor);
		}
		proxy.css({
			left : e.data.left,
			top : e.data.top
		});
	}
	;
	function doDown(e) {
		isDown = true;
		var opts = $.data(e.data.target, "draggable").options;
		// 获取当前对象可被拖动到的目标区域
		var droppables = $(".droppable").filter(function() {
			return e.data.target != this;
		}).filter(function() {
			var accept = $.data(this, "droppable").options.accept;
			if (accept) {
				return $(accept).filter(function() {
					return this == e.data.target;
				}).length > 0;
			} else {
				return true;
			}
		});
		$.data(e.data.target, "draggable").droppables = droppables;
		var proxy = $.data(e.data.target, "draggable").proxy;
		if (!proxy) {
			if (opts.proxy) {
				if (opts.proxy == "clone") {
					proxy = $(e.data.target).clone().insertAfter(e.data.target);
				} else {
					proxy = opts.proxy.call(e.data.target, e.data.target);
				}
				$.data(e.data.target, "draggable").proxy = proxy;
			} else {
				proxy = $(e.data.target);
			}
		}
		proxy.css("position", "absolute");
		drag(e);
		applyDrag(e);
		opts.onStartDrag.call(e.data.target, e);
		return false;
	}
	;
	function doMove(e) {
		drag(e);
		if ($.data(e.data.target, "draggable").options.onDrag.call(
				e.data.target, e) != false) {
			applyDrag(e);
		}
		var source = e.data.target;
		$.data(e.data.target, "draggable").droppables.each(function() {
			var dropObj = $(this);
			var p2 = $(this).offset();
			if (e.pageX > p2.left && e.pageX < p2.left + dropObj.outerWidth()
					&& e.pageY > p2.top
					&& e.pageY < p2.top + dropObj.outerHeight()) {
				if (!this.entered) {
					$(this).trigger("_dragenter", [ source ]);
					this.entered = true;
				}
				$(this).trigger("_dragover", [ source ]);
			} else {
				if (this.entered) {
					$(this).trigger("_dragleave", [ source ]);
					this.entered = false;
				}
			}
		});
		return false;
	}
	;
	function doUp(e) {
		isDown = false;
		drag(e);
		var proxy = $.data(e.data.target, "draggable").proxy;
		var opts = $.data(e.data.target, "draggable").options;
		if (opts.revert) {
			if (checkDrop() == true) {
				removeProxy();
				$(e.data.target).css({
					position : e.data.startPosition,
					left : e.data.startLeft,
					top : e.data.startTop
				});
			} else {
				if (proxy) {
					proxy.animate({
						left : e.data.startLeft,
						top : e.data.startTop
					}, function() {
						removeProxy();
					});
				} else {
					$(e.data.target).animate({
						left : e.data.startLeft,
						top : e.data.startTop
					}, function() {
						$(e.data.target).css("position", e.data.startPosition);
					});
				}
			}
		} else {
			$(e.data.target).css({
				position : "absolute",
				left : e.data.left,
				top : e.data.top
			});
			removeProxy();
			checkDrop();
		}
		opts.onStopDrag.call(e.data.target, e);
		$(document).unbind(".draggable");
		setTimeout(function() {
			$("body").css("cursor", "auto");
		}, 100);
		function removeProxy() {
			if (proxy) {
				proxy.remove();
			}
			$.data(e.data.target, "draggable").proxy = null;
		}
		;
		function checkDrop() {
			var dropped = false;
			$.data(e.data.target, "draggable").droppables.each(function() {
				var dropObj = $(this);
				var p2 = $(this).offset();
				if (e.pageX > p2.left
						&& e.pageX < p2.left + dropObj.outerWidth()
						&& e.pageY > p2.top
						&& e.pageY < p2.top + dropObj.outerHeight()) {
					if (opts.revert) {
						$(e.data.target).css({
							position : e.data.startPosition,
							left : e.data.startLeft,
							top : e.data.startTop
						});
					}
					$(this).trigger("_drop", [ e.data.target ]);
					dropped = true;
					this.entered = false;
				}
			});
			return dropped;
		}
		;
		return false;
	}
	;
	$.fn.draggable = function(optOrMth, mthParam) {
		if (typeof optOrMth == "string") {
			return $.fn.draggable.methods[optOrMth](this, mthParam);
		}
		return this.each(function() {
			var opts;
			var data = $.data(this, "draggable");// 获取存放在元素上的数据
			if (data) {
				data.handle.unbind(".draggable");
				opts = $.extend(data.options, optOrMth);
			} else {
				opts = $.extend({}, $.fn.draggable.defaults, optOrMth || {});
			}
			if (opts.disabled == true) {
				$(this).css("cursor", "default");
				return;
			}
			var handle = null;
			if (typeof opts.handle == "undefined" || opts.handle == null) {
				handle = $(this);
			} else {
				handle = (typeof opts.handle == "string" ? $(opts.handle, this)
						: opts.handle);
			}
			// 缓存数据，包括opts,handle
			$.data(this, "draggable", {
				options : opts,
				handle : handle
			});
			handle.unbind(".draggable").bind("mousemove.draggable", {
				target : this
			}, function(e) {
				if (isDown) {
					return;
				}
				var opts = $.data(e.data.target, "draggable").options;
				if (checkArea(e)) {
					$(this).css("cursor", opts.cursor);
				} else {
					$(this).css("cursor", "");
				}
			}).bind("mouseleave.draggable", {
				target : this
			}, function(e) {
				$(this).css("cursor", "");
			}).bind("mousedown.draggable", {
				target : this
			}, function(e) {
				if (checkArea(e) == false) {
					return;
				}
				var relativePosition = $(e.data.target).position();
				var originData = {// 记录目标元素相对父元素的位置，以及鼠标按下时的位置，同时记录目标元素和父元素。
					/*
					 * 以start开头的，是记录最原始的位置值，这样可以回到原始位置
					 */
					startPosition : $(e.data.target).css("position"),
					startLeft : relativePosition.left,
					startTop : relativePosition.top,
					/*
					 * left和top用于记录动态变化的位置值，随着拖动而改变
					 */
					left : relativePosition.left,
					top : relativePosition.top,
					startX : e.pageX,
					startY : e.pageY,
					target : e.data.target,
					parent : $(e.data.target).parent()[0]
				};
				$.extend(e.data, originData);
				var opts = $.data(e.data.target, "draggable").options;
				if (opts.onBeforeDrag.call(e.data.target, e) == false) {
					return;
				}
				$(document).bind("mousedown.draggable", e.data, doDown);
				$(document).bind("mousemove.draggable", e.data, doMove);
				$(document).bind("mouseup.draggable", e.data, doUp);
				$("body").css("cursor", opts.cursor);
			});
			function checkArea(e) {
				var data = $.data(e.data.target, "draggable");
				var handle = data.handle;
				var offset = $(handle).offset();// 获取匹配元素在当前视口的相对偏移
				var outerWidth = $(handle).outerWidth();
				var outerHeight = $(handle).outerHeight();
				var t = e.pageY - offset.top;
				var r = offset.left + outerWidth - e.pageX;
				var b = offset.top + outerHeight - e.pageY;
				var l = e.pageX - offset.left;
				return Math.min(t, r, b, l) > data.options.edge;
			}
			;
		});
	};
	$.fn.draggable.methods = {
		options : function(jq) {
			return $.data(jq[0], "draggable").options;
		},
		proxy : function(jq) {
			return $.data(jq[0], "draggable").proxy;
		},
		enable : function(jq) {
			return jq.each(function() {
				$(this).draggable({
					disabled : false
				});
			});
		},
		disable : function(jq) {
			return jq.each(function() {
				$(this).draggable({
					disabled : true
				});
			});
		}
	};
	$.fn.draggable.defaults = {
		proxy : null,
		revert : false,// 是否拖动后返回到原位置
		cursor : "move",// 拖动时鼠标样式
		deltaX : null,// 鼠标距离移动元素的x轴距离
		deltaY : null,// 鼠标距离移动元素的y轴距离
		handle : null,// 可拖动位置
		disabled : false,// 是否失效
		edge : 0,// 可拖动处距离边缘的距离
		axis : null,// 移动方向，默认任意方向
		onBeforeDrag : function(e) {
		},
		onStartDrag : function(e) {
		},
		onDrag : function(e) {
		},
		onStopDrag : function(e) {
		}
	};
})(jQuery);
